package org.edu.common.base;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.apache.shiro.SecurityUtils;
import org.edu.common.system.vo.LoginUser;
import org.edu.common.utils.ConvertUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.function.Function;

/**
 * 实现类
 */
@Slf4j
public abstract class BaseServiceImpl<T, Dao extends BaseMapper<T>> implements BaseService<T> {
    /**
     * dao
     */
    @Autowired
    protected Dao mapper;

    /**
     * 批量操作记录数
     */
    protected int batchSize = 500;

    /**
     * 实体类
     */
    protected Class<T> tClass;

    /**
     * 表信息
     */
    protected TableInfo tableInfo;



    public BaseServiceImpl() {
        //获取实体类
        tClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
     * 初始化
     *

     * @since 2020-05-06
     */
    @PostConstruct
    private void init() {
        tableInfo = SqlHelper.table(tClass);
    }

    @Override
    public void  insert(T entity) {
        if(entity instanceof  BaseEntity){
            BaseEntity base =  (BaseEntity)entity;
            LoginUser loginUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
            if (loginUser != null && loginUser.getId() != null) {
                base.setCreateBy(loginUser.getId());
                base.setCreateName(loginUser.getRealname());
            }
        }
        mapper.insert(entity);
    }

    @Override
    public void insert(Collection<? extends T> list) {
        execute(list, (EventAssignIsInsert<T>) entity -> true);
    }

    @Override
    public void update(T entity) {
        mapper.updateById(entity);
    }

    @Override
    public void update(Wrapper wrapper) {
        mapper.update(null,wrapper);
    }

    @Override
    public void update(Collection<? extends T> list) {
        execute(list, (EventAssignIsInsert<T>) entity -> false);
    }

    @Override
    public void save(T entity) {
        boolean insert = isInsert(entity);
        if (insert) {
            insert(entity);
        } else {
            update(entity);
        }
    }

    @Override
    public boolean delete(String id) {
        return mapper.deleteById(id) == 1;
    }

    @Override
    public int delete(Collection<String> ids) {
        if (ids == null || ids.isEmpty()) {
            return 0;
        }
        return mapper.deleteBatchIds(ids);
    }

    @Override
    public T findById(String id) {
        return mapper.selectById(id);
    }

    @Override
    public <R> R findById(String id, Class<R> rClass) {
        T entity = mapper.selectById(id);
        return ConvertUtils.convertToDto(entity, rClass);
    }

    @Override
    public List<T> findAll() {
        return mapper.selectList(null);
    }

    @Override
    public IPage page(BasePageDto page, Wrapper<T> queryWrapper) {
        return mapper.selectPage(page, queryWrapper);
    }

    @Override
    public <R> IPage<R> page(BasePageDto page, Wrapper<T> queryWrapper, Function<T, R> convertMapper) {
        IPage<T> tiPage = mapper.selectPage(page, queryWrapper);
        return tiPage.convert(convertMapper);
    }

    @Transactional(readOnly = true)
    @Override
    public List<T> listByWrapper(Wrapper<T> wrapper) {
        return mapper.selectList(wrapper);
    }

    /**
     * 判断是否新增
     *
     * @param entity 实体
     * @return boolean
     */
    private boolean isInsert(T entity) {
        Object idVal = ReflectionKit.getFieldValue( entity, tableInfo.getKeyProperty());
        return Objects.isNull(idVal) || StrUtil.isBlank(idVal.toString());
    }


    private void execute(Collection<? extends T> list) {
        execute(list, null);
    }


    private void execute(Collection<? extends T> list, Event... events) {
        if (list != null && !list.isEmpty()) {
            try (SqlSession sqlSession = SqlHelper.sqlSessionBatch(tClass)) {
                int i = 0;
                for (T entity : list) {
                    execute(sqlSession, entity, events);
                    if (i >= 1 && i % batchSize == 0) {
                        sqlSession.flushStatements();
                    }
                    i++;
                }
                sqlSession.flushStatements();
            }
        }
    }
    /**
     * sqlSession方式执行
     *
     * @param sqlSession sqlSession
     * @param entity     实体
     * @param isInsert   是否新增
     */
    private void execute(SqlSession sqlSession, T entity, boolean isInsert) {
        if (isInsert) {
            sqlSession.insert(tableInfo.getSqlStatement(SqlMethod.INSERT_ONE.getMethod()), entity);
        } else {
            MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
            param.put(Constants.ENTITY, entity);
            sqlSession.update(tableInfo.getSqlStatement(SqlMethod.UPDATE_BY_ID.getMethod()), param);
        }
    }

    /**
     * sqlSession方式执行
     *
     * @param sqlSession sqlSession
     * @param entity     实体
     * @param events     事件
     */
    private void execute(SqlSession sqlSession, T entity, Event... events) {
        boolean isInsert = doEventAssignIsInsert(entity, events);
        execute(sqlSession, entity, isInsert);
    }

    /**
     * 执行判断是否新增
     *
     * @param entity 实体
     * @param events 事件
     * @return boolean 是否新增
     */
    protected boolean doEventAssignIsInsert(T entity, Event... events) {
        if (events != null) {
            final Optional<Event> first = Arrays.stream(events).filter(event -> event instanceof EventAssignIsInsert).findFirst();
            if (first.isPresent()) {
                return ((EventAssignIsInsert) first.get()).isInsert(entity);
            }
        }
        return isInsert(entity);
    }


    /**
     * 事件
     */
    interface Event {
    }

    /**
     * 事件-指定是否为新增操作
     */
    interface EventAssignIsInsert<T> extends Event {
        boolean isInsert(T entity);
    }

    @Override
    public void importExcel(List<Map<String, Object>> mapList){

    }
}
